﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Runtime.Serialization;

namespace PedesTrainProject.Traffic
{
    [Serializable()]
    public class Car : TrafficMember, ISerializable
    {
        public int acceleration = 0;
        public int current_speed = 0;
        public int max_speed = 1;
        public List<Grid.Directions> path = new List<Grid.Directions>();

        public int tile_index;
        public int dest_index;
        public int lane_index;
        public int slot_index;
        public Grid.Directions dirGo;
        public int offestX,offsetY;

        private int step;

        private bool alive;

        public Car(string _skin, int _tile_index, int _lane_index, int _dest_index, Core.ICoreTraffic _coreTraffic)
        {
            skin = _skin;
            tile_index = _tile_index;
            lane_index = _lane_index;
            dest_index = _dest_index;
            slot_index = 0;
            step = 0;
            alive = true;

            coreTraffic = _coreTraffic;
            Grid.Grid grid = coreTraffic.getGrid();

            List<bool> bool_grid = new List<bool>();
            for (int i = 0; i < coreTraffic.getGrid().Height * coreTraffic.getGrid().Width; i++) bool_grid.Add(false);

            generatePath(grid, tile_index, dest_index, bool_grid);

           
            position = Core.Calculations.calculateCarCoordinates(grid.Tiles[tile_index], tile_index, lane_index);

         //   for (int i = 0; i < path.Count; i++) Console.WriteLine(i + ": " + path[i]);
        }

        public Car(string _skin, Grid.Tile _starter, Grid.Tile _destination, Vector _position)
        {

        }

        public Car(string _skin, Grid.Tile _destination, Vector _absolute_position, int _max_speed)
        {

        }

        public Car(string _skin, Grid.Tile _starter, Grid.Tile _destination, Vector _position, int _max_speed)
        {

        }

        public Car(string _skin, Grid.Tile _destination, Vector _absolute_position, int _max_speed, int _current_speed, int _acceleration)
        {

        }

        public Car(string _skin, Grid.Tile _starter, Grid.Tile _destination, Vector _position, int _max_speed, int _current_speed, int _acceleration)
        {
            //calculatePath(
        }

        public KeyValuePair<Vector, bool> go(Grid.Directions direction)
        {
            if (current_speed < max_speed)
            {
                current_speed++;
                //acceleration++;
            }
            else
            {
                current_speed = max_speed;
                //acceleration = 0;
            }
            return Core.Calculations.calculateCarCoordinates(position, direction, current_speed);
        }

        public KeyValuePair<Vector, bool> stop(Grid.Directions direction)
        {
            if (current_speed > 0)
            {
                current_speed-- ;
                //acceleration--;
            }
            else
            {
                current_speed = 0;
                //acceleration = 0;
            }
            return Core.Calculations.calculateCarCoordinates(position, direction, current_speed);
        }

        public void animate()
        {
           // if(coreTraffic.getGrid().Tiles[tile_index].Road.Name!="straight" && 
            //Console.WriteLine("animate\tposition:\tx = " + position.X + "\ty = " + position.Y);
            coreTraffic.animateCar(position);
        }

        public bool move()
        {
            int temp_tile_index = tile_index;
            int temp_lane_index = lane_index;
            int temp_slot_index = slot_index;
            int temp_step = step;

            if (checkLights())
            {
                bool path_direction = findNextSlot();

                if (!alive || step > path.Count) return false;
                else
                {
                   // Console.WriteLine("Tile: " + tile_index + ", Lane: " + lane_index + ", Slot: " + slot_index + "; Step: " + step + "/" + path.Count);
                    if (coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access)
                    {
                        KeyValuePair<Vector, bool> kvp_go = new KeyValuePair<Vector, bool>();
                        if (path_direction) kvp_go = go(path[step]);//FAKE
                        else kvp_go = go(coreTraffic.getGrid().Tiles[temp_tile_index].Road.Lanes[temp_lane_index].Direction);//
                        // else kvp_go = go(dirGo);
                        //   else kvp_go = go(Grid.Directions.East);
                        if (kvp_go.Value)
                        {
                            coreTraffic.getGrid().Tiles[temp_tile_index].Road.Lanes[temp_lane_index].Slots[temp_slot_index].Car_access = true;
                            coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access = false;
                            
                            if ((coreTraffic.getGrid().Tiles[tile_index].Road.Name == "crossing" ||
                                    coreTraffic.getGrid().Tiles[tile_index].Road.Name == "Tcrossing") && slot_index == 0)
                            {
                                coreTraffic.releaseJam(tile_index);
                                Console.WriteLine("Release " + tile_index);
                            }
                        }
                        else
                        {
                            tile_index = temp_tile_index;
                            lane_index = temp_lane_index;
                            slot_index = temp_slot_index;
                            step = temp_step;
                        }

                        position = kvp_go.Key;
                        // Console.WriteLine("go\ttile = " + tile_index + "\tlane = " + lane_index + "\tslot = " + slot_index);
                        return true;
                    }
                    else // no car access
                    {
                        if ((coreTraffic.getGrid().Tiles[tile_index].Road.Name == "crossing" ||
                                coreTraffic.getGrid().Tiles[tile_index].Road.Name == "Tcrossing") && slot_index == 0)
                        {
                            coreTraffic.addJam(tile_index);
                            Console.WriteLine("Add " + tile_index);
                        }
                    }
                }

            }
            else
            {
                tile_index = temp_tile_index;
                lane_index = temp_lane_index;
                slot_index = temp_slot_index;
                step = temp_step;
                KeyValuePair<Vector, bool> kvp_stop = stop(coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Direction);
             //   Console.WriteLine("Tile: " + tile_index + ", Lane: " + lane_index + ", Slot: " + slot_index + "; Step: " + step + "/" + path.Count);
                position = kvp_stop.Key;
            }

           
            return true;
        }

        private bool checkLights()
        {
            if (slot_index + 1 < coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots.Count)
            {
                Grid.Slot temp_slot = coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index + 1];
                if (temp_slot.Lights)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        Grid.CarLight light = coreTraffic.getGrid().Tiles[tile_index].Road.CarLights[i];
                        if (light.Direction == coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Direction) return !light.Red;
                    }
                }
            }
            return true;
        }

        private bool findNextSlot()
        {
          //  Console.WriteLine("stop\ttile = " + tile_index + "\tlane = " + lane_index + "\tslot = " + slot_index);
            Grid.Tile temp_tile = coreTraffic.getGrid().Tiles[tile_index];
            coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access = true;
            if (slot_index < temp_tile.Road.Lanes[lane_index].Slots.Count -1  && lane_index != 8 && lane_index != 9)
            {
                slot_index++;
                //Console.WriteLine("PRZYNAJMNIEJ TO UMIEM");
              //  Console.WriteLine(slot_index + " " + temp_tile.Road.Lanes[lane_index].Slots.Count + " " + temp_tile.Road.Lanes[lane_index].Exit + " " + lane_index + " " + tile_index);
                return false;
            }
            else
            {
                //Console.WriteLine("TUTAJ KURWA TEZ WCHOZE O LOL");
               
                if (temp_tile.Road.Lanes[lane_index].Exit) // next tile
                {
                    //Console.WriteLine("WCHODZE TU KURWA");
                    int next_tile_index = tile_index;

                    if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.North)
                    {
                     //   next_tile_index -= coreTraffic.getGrid().Width;
                        if (next_tile_index < 4)
                        {
                            //Console.WriteLine("North");
                            coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access = true;
                            alive = false;
                            next_tile_index = -1;
                        }
                        else next_tile_index -= coreTraffic.getGrid().Width;
                    }
                    else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.South)
                    {
                       // next_tile_index += coreTraffic.getGrid().Width;
                        if (next_tile_index > 11)
                        {
                            //Console.WriteLine("South");
                            coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access = true;
                            alive = false;
                            next_tile_index = -1;
                        }
                        else next_tile_index += coreTraffic.getGrid().Width;
                    }
                    else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.East)
                    {
                       
                        if ((next_tile_index + 1) % coreTraffic.getGrid().Width == 0)
                        {
                            //Console.WriteLine("East");
                            coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access = true;
                            alive = false;
                            next_tile_index = -1;
                        }
                        else next_tile_index++; 
                    }
                    else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.West)
                    {
                        // next_tile_index--;
                      //  Console.WriteLine(next_tile_index);
                        if ((next_tile_index) % coreTraffic.getGrid().Width == 0)
                        {
                            //Console.WriteLine("West");
                            coreTraffic.getGrid().Tiles[tile_index].Road.Lanes[lane_index].Slots[slot_index].Car_access = true;
                            alive = false;
                            next_tile_index = -1;
                        }
                        else next_tile_index--;
                    }

                    if (next_tile_index >= 0 && next_tile_index < coreTraffic.getGrid().Tiles.Count)
                    {
                        tile_index = next_tile_index;
                        Grid.Tile temp_temp_tile = coreTraffic.getGrid().Tiles[tile_index];
                        if (temp_temp_tile.Road.Name == "empty")
                        {
                            //Console.WriteLine("empty");
                            alive = false;
                        }
                        /*else if (temp_temp_tile.Road.Name == "corner")
                        {
                            if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.North) lane_index = 0;
                            else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.East) lane_index = 1;
                            else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.South) lane_index = 2;
                            else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.West) lane_index = 3;
                        }*/
                        else if (temp_temp_tile.Road.Name != "straight")
                        {
                            if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.South) lane_index = 0;
                            else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.West) lane_index = 2;
                            else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.North) lane_index = 4;
                            else if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.East) lane_index = 6;
                        }
                        else
                        {
                            if (temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.South || temp_tile.Road.Lanes[lane_index].Direction == Grid.Directions.West) lane_index = 0;
                            else lane_index = 1;
                        }
                        slot_index = 0;
                        step++;
                    }
                    else
                    {
                        //Console.WriteLine("end");
                        alive = false;
                    }
                }
                else // is never straight
                {
                    Grid.Directions dir = temp_tile.Road.Lanes[lane_index].Direction;
                    //Console.WriteLine(dir + "/" + path[step]);

                    if (lane_index == 8 || lane_index == 9) // middle of the crossing/turn
                    {
                            if (path[step] == dir) // go straight
                        {
                            if (slot_index == 0) { slot_index = 1; }
                            else
                            {
                                if (dir == Grid.Directions.East) lane_index = 3;
                                else if (dir == Grid.Directions.West) lane_index = 7;
                                else if (dir == Grid.Directions.North) lane_index = 1;
                                else if (dir == Grid.Directions.South) lane_index = 5;
                                slot_index = 0;
                                return true; //new
                            }
                        }    
                        else // turn
                        {
                            Grid.Directions dir_temp = dir;

                          //  if (dir == Grid.Directions.West) dir = Grid.Directions.North;
                          //  else dir++;
                            if (dir == Grid.Directions.West) dir = Grid.Directions.North;
                            else dir++;
                            
                            if (dir == path[step]) // right turn and left turn 3rd step
                            {
                                if (dir == path[step])
                                {
                                    slot_index = 0;
                                    if (dir == Grid.Directions.East)
                                    {
                                        lane_index = 3;
                                    }
                                    else if (dir == Grid.Directions.West)
                                    {
                                        lane_index = 7;
                                    }
                                    else if (dir == Grid.Directions.North) 
                                    { 
                                        lane_index = 1; 
                                    }
                                    else if (dir == Grid.Directions.South)
                                    {
                                        lane_index = 5;
                                    }
                                    dirGo = dir;
                                    
                                }
                                else
                                {
                                    slot_index = 1;
                                    if (dir == Grid.Directions.East) lane_index = 3;
                                    else if (dir == Grid.Directions.West) lane_index = 7;
                                    else if (dir == Grid.Directions.North) lane_index = 0;
                                    else if (dir == Grid.Directions.South) lane_index = 5;
                                }
                                return true;
                            }
                            else // left turn
                            {
                               // Console.WriteLine("W LEWO, KURWA, W LEWO");  
                                if (slot_index == 0)
                                {
                                    if (lane_index == 9)
                                    {
                                        slot_index = 1;
                                        return false;
                                    }
                              //      if (lane_index == 8)
                                //    {
                                  //      if (dir_temp == Grid.Directions.South)
                                    //    {
                                      //      slot_index = 1;
                                       //     return false;
                                       // }
                                       // else return false;
                                        
                                        //return false;
                                  //  }
                                   
                                    if (dir_temp == Grid.Directions.West && path[step] == Grid.Directions.North)
                                    {
                                        slot_index = 0;
                                        return true;
                                    }
                                    else
                                    {
                                        slot_index = 1;
                                        return true;
                                    }
                                   
                                }// 1st step
                                else // 2nd step
                                {
                                  //  dirGo = dir;
                                    if (lane_index == 8)
                                    {
                                        lane_index = 9;
                                        if (dir_temp == Grid.Directions.West) return false;
                                        else return false;
                                    } // was =9
                                    else
                                    {
                                        lane_index = 8;
                                        slot_index = 0;
                                        if (dir_temp == Grid.Directions.East) return true;
                                        else return false;
                                    }
                                    //    slot_index = 1;
                                    //    dir = Grid.Directions.West;
                                    return true;
                                    
                                }
                            }
                        }
                      //  if (lane_index == 9) slot_index = 0;
                        return true;
                    }
                    else // beginning of the crossing/turn
                    {
                        if (dir == Grid.Directions.West || dir == Grid.Directions.South)
                        {
                            lane_index = 8;
                          //  return true;
                        }
                        else lane_index = 9;

                        if (dir == temp_tile.Road.Lanes[lane_index].Direction) { slot_index = 0;  }
                        else { slot_index = 1;  }
                    }
                }
                //Console.WriteLine(slot_index + " " + temp_tile.Road.Lanes[lane_index].Slots.Count + " " + temp_tile.Road.Lanes[lane_index].Exit + " " + lane_index + " " + tile_index);
                return false;
            }
            
        }

        private bool generatePath(Grid.Grid temp_grid, int current, int destination, List<bool> bool_grid)
        {
            bool_grid[current] = true;
            if (current == destination) return true;

            List<Grid.Directions> directions = new List<Grid.Directions>();
            foreach (Grid.Lane lane in temp_grid.Tiles[current].Road.Lanes) if (lane.Exit) directions.Add(lane.Direction);

            List<Grid.Directions> temp = new List<Grid.Directions>();
            if (directions.Count > 2)
            {
              //  Console.WriteLine("Randomizing list...");
                Random random = new Random();
                for (int i = directions.Count - 1; i >= 0; i--)
                {
                    int rand = random.Next(0, i);
                    //Console.WriteLine(directions[rand] + " / " + rand + " / " + i);
                    temp.Add(directions[rand]);
                    directions.RemoveAt(rand);
                }
            }
            else temp = directions;

          //  Console.WriteLine(current + "/" + destination + "/" + temp.Count);
            for (int i = 0; i < temp.Count; i++)
            {
                if (temp[i] == Grid.Directions.North)
                {
                    if (current - 4 >= 0)
                    {
                        if (!bool_grid[current - 4])
                        {
                            path.Add(Grid.Directions.North);
                            if (generatePath(temp_grid, current - 4, destination, bool_grid)) return true;
                            else path.RemoveAt(path.Count - 1);
                        }
                    }
                }
                else if (temp[i] == Grid.Directions.East)
                {
                    if ((current + 1) % 4 != 0)
                    {
                        if (!bool_grid[current + 1])
                        {
                            path.Add(Grid.Directions.East);
                            if (generatePath(temp_grid, current + 1, destination, bool_grid)) return true;
                            else path.RemoveAt(path.Count - 1);
                        }
                    }
                }
                else if (temp[i] == Grid.Directions.South)
                {
                    if (current + 4 < 16)
                    {
                        if (!bool_grid[current + 4])
                        {
                            path.Add(Grid.Directions.South);
                            if (generatePath(temp_grid, current + 4, destination, bool_grid)) return true;
                            else path.RemoveAt(path.Count - 1);
                        }
                    }
                }
                else if (temp[i] == Grid.Directions.West)
                {
                    if (current % 4 != 0)
                    {
                        if (!bool_grid[current - 1])
                        {
                            path.Add(Grid.Directions.West);
                            if (generatePath(temp_grid, current - 1, destination, bool_grid)) return true;
                            else path.RemoveAt(path.Count - 1);
                        }
                    }
                }
            }
            bool_grid[current] = false;
            return false;
        }

        #region Serialize
        public Car(SerializationInfo info, StreamingContext ctxt)
        {
            this.position = (Vector)info.GetValue("Position", typeof(Vector));
            this.absolute_position = (Vector)info.GetValue("Absolute_Position", typeof(Vector));
            this.starter = (int)info.GetValue("Starter", typeof(int));
            this.destination = (int)info.GetValue("Destination", typeof(int));
            this.skin = (string)info.GetValue("Skin", typeof(string));
            this.acceleration = (int)info.GetValue("Acceleration", typeof(int));
            this.current_speed = (int)info.GetValue("Current_Speed", typeof(int));
            this.max_speed = (int)info.GetValue("Max_Speed", typeof(int));
            this.path = (List<Grid.Directions>)info.GetValue("Path", typeof(List<Grid.Directions>));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Position", this.position);
            info.AddValue("Absolute_Position", this.absolute_position);
            info.AddValue("Starter", this.starter);
            info.AddValue("Destination", this.destination);
            info.AddValue("Skin", this.skin);
            info.AddValue("Acceleration", this.acceleration);
            info.AddValue("Current_Speed", this.current_speed);
            info.AddValue("Max_Speed", this.max_speed);
            info.AddValue("Path", this.path);
        }
        #endregion

    }
}
